#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#define STDIN_FID 0
#define STDOUT_FID 1

int parsing(char line[], char* args[], char type1[], char type2[]);
void command(char* args[], char* path);
void seek(char *arreglo[]);
void changeDir(char* args[], char* path);
void showPrompt();
int whichstatus(char* args[]);
void redirection(char* file_name);
void searchDot(char *args[]);
void searchPath(char *args[]);
void pipee(char *args[]);

int cantargs;
char* directories[100];
char* PATH;
int flagstatus;
int position;

int main(int argc, char *argv[]){
    printf("--------------------------BAASH--------------------------\n");
    char* args[100];   
    char line[1024];
    
    PATH = getenv("PATH");
    parsing(PATH, directories, ":",":");
        
    while(1){  
        flagstatus=0;
        showPrompt();                  
                if (fgets(line, 1024, stdin) == NULL) return 0;     //Exit
                else{
                        cantargs=parsing(line, args, " ", " \n");       //Parsea la linea de entrada.
                        position=whichstatus(args);
                        command(args, getcwd(NULL, 0));                         //sino, interpreta el comando (agrs[0])
                }        
    }
    return 0;
}

/*
 * De acuerdo a lo ingreado, testea si se ingreso < > | ó & y cambia flagstatus
 * @param char* args[]
 * @return int
 */
int whichstatus(char* args[]){
        int i;
        for (i=0;i<cantargs;i++){
                if(strcmp(args[i],"&")==0){
                        args[i] = NULL;
                        cantargs--;
                        flagstatus=1;
                        return i;
                }
                if(strcmp(args[i],"<")==0){
                        args[i] = NULL;
                        cantargs--;
                        flagstatus=2;
                        return i;
                }
                if(strcmp(args[i],">")==0){
                        args[i] = NULL;
                        cantargs--;
                        flagstatus=3;
                        return i;
                }
                if(strcmp(args[i],"|")==0){
                        flagstatus=4;
                        return i;
                }
        }return i;
}

/*
 * Muestra el prompt
 * @param void
 * @return void
 */
void showPrompt(){
    printf("%s # %s > ", getenv("LOGNAME"),(char*) getcwd(NULL, 0));            
}

/*
 * Parsea una linea, de acuerdo a los parametros recibidos
 * @param char line
 * @param char* args[]
 * @param char type1[]
 * @param char type2[]
 * @return void
 */
int parsing(char line[], char* args[], char type1[], char type2[]){ 
    int i = 0;
    args[i] = strtok(line, type1);    
    while((args [i]) != NULL) {
                i++;
        args[i] = strtok (NULL, type2);
    }
    return i;
}

/*
 * Interpreta el comando: si es cd llama a changedir(), si no es cd llama a seek().
 * @param char* args[]
 * @param char* path
 * @return void
 */
void command(char* args[], char* path){
    if(strcmp(args[0], "exit\n") == 0) exit(1);
    if(strcmp(args[0], "\n") == 0) return;
    if(strcmp(args[0], "cd") == 0) changeDir(args, path);
        else seek(args);
}

/*
 * Cambia el directorio (comando cd)
 * @param char* args[]
 * @param char* path
 * @return void
 */
void changeDir(char* args[], char* path){
    if( chdir(args[1]) != 0 ) perror("Command not found\n");
        else path = (char*) getcwd(NULL, 0);
}

/*
 * Evaulua flagstatus, y manda a ejecutar el parametro recibido
 * @param char *args[]
 * @return void
 */
void seek(char *args[]){
    int flag = 0;
    pid_t pid;

    
    if (flagstatus==4) pipee(args);                     //Si tiene | llama a pipee 
    else{
                if ( (pid = fork()) == 0){  // HIJO ---------------------------------------------------------------------------------------
                
                        if(flagstatus==2||flagstatus==3){
                                redirection(args[position+1]);
                                args[position+1]=NULL;
                        }
                        if(args[0][0]=='.')searchDot(args);//Si comienza con un ./ o   ../
							else searchPath(args);                  //Si no comienza con ./ o ../
                }
                else if ( pid>0 )flag =-1; // PADRE ---------------------------------------------------------------------------------------
                
                if(flagstatus!=1){                                     //Si no habia &, espera al hijo
                        waitpid(pid, &flag, 0);         
                        args=NULL;                      
                }
                else{                                                  //Si habia &, no lo espera
                        args = NULL;
                        waitpid(pid, &flag, WNOHANG);
                }
        }  
}

/*
 * Redireccion de I/O, si hay < ó >
 * @param char *filename
 * @return void
 */
void redirection(char* file_name){
        int fid;

        if(flagstatus ==2){
                fid = open(file_name, O_RDONLY, S_IWUSR|S_IRUSR);
                if(fid<0) perror("open");
                close(STDIN_FID);
                if(dup(fid)<0)perror("dup");
        }

        if(flagstatus ==3){
                fid = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
                if(fid<0) perror("open");
                close(STDOUT_FID);
                if(dup(fid)<0)perror("dup");
        }
}    
  
/*
 * Busca el parametro recibido en directorio actual o directorios superiores    
 * @param char *args[]
 * @return void
 */
void searchDot(char *args[]){
        char str[200];
        char * aux1;
    char * aux2;
    int status = 0;
    
    aux1 = strtok(args[0],"/");     
    aux2 = aux1;
    
    while((status == 0)&&((aux1 = strtok(NULL,"/"))!= NULL)){
        status = chdir(aux2);   
        aux2 = aux1;            
        }                                       
    
        char* path = (char*) getcwd(NULL, 0);   //Encuentra el pwd.
        strcat (path,"/");
        strcat (path,aux2);     
        strncpy(str,path,sizeof(str));
    
        //Quita el final de linea, solo si no habia & (ya que si habia, fue quitado):
        if (flagstatus==0){              
                int j=0;
        int n=0;
        while(str[j]!='\0')j++;                
        for(n=0;n<j;n++) if((str[n]=='\n')||(str[n]=='\0'))str[n]='\0';
        }
                        
        if((access(str,F_OK))==0){
                execv(str, args);
        }
        else{
                perror("ERROR, File not found \n");
                exit(1);
        }
} 

/*
 * Busca el parametro recibido en PATH  
 * @param char *args[]
 * @return void
 */
void searchPath(char *args[]){
        int num = 0;
        char str[200];
        int i = 0;        
        char* dir[100];
                
        while( directories[i] != NULL){                    
                //Busca en PATH el comando especificado:    
                //Arma el string = path + / + comando:    
                dir[i] = directories[i];
                strcpy (str, dir[i]);                            
                strcat (str, "/");
                strcat(str, args[0]);

                //Quita el final de linea, solo si no habia & (ya que si habia, fue quitado):
                if (flagstatus==0){
                        int j = 0;
                        while(str[j]!='\0')j++;                
                        if ( args[1] != NULL ) str[j] = '\0';
				else str[j-1] = '\0';
                }
                
                //Si lo encuentra lo ejecuta:
                if( (access(str,F_OK)) == 0 ){
                        execv(str, args);
                        num = 1;
        }
                else i++;       //Si no lo encientra incrementa i, para buscar en otro directorio.
        }
             
        //Si no encontro el comando en todo PATH muestra un mensaje de error:
        if( num == 0 ){
                perror("ERROR, File not found ");
        exit(1);
        }
}

/*
 * Ejecuta dos procesos comunicados entre si (por haber |)      
 * @param char *args[]
 * @return void
 */
void pipee(char *args[]){
        
        char* args1[position];
        char* args2[cantargs-position];
        int i,j;
        int x=0;
        int fork_id;
        int fork_id2;
        int server[2];
        
        //Divido args[0] en dos: args1[] y args2[], separados por |
        for (i=0;i<position;i++){
                        args1[i]=args[i];
                        args1[i+1]=NULL;
        }
        for (j=i+1;j<cantargs;j++){
                        args2[x]=args[j];
                        args2[x+1]=NULL;
                        x++;
        }
        
        pipe(server); 
        //Crea el primer hijo, para que ejecute args1
          
        if((fork_id=fork())<0)printf("Error in proc1");
        if(fork_id == 0){
                close(1);
                dup(server[1]);
                close(server[0]); close(server[1]);
                if (args1[0][0]=='.')searchDot(args1);        
					else searchPath(args1);
        }
        
        //Crea el segundo hijo, para que ejecute args2
        if((fork_id2=fork())<0)printf("Error in proc1");
        if(fork_id2==0){        
                close(0);
                dup(server[0]);
                close(server[0]); 
                close(server[1]);
                if (args2[0][0]=='.')searchDot(args2);
					else searchPath(args2);
        }
        
        close (server[0]);
        close(server[1]);
        wait(&fork_id);
        wait(&fork_id2);
        
}
